package cn.iotab.iotkit.transport.mqtt.ssl;


import cn.iotab.iotkit.common.util.EncryptionUtil;
import cn.iotab.iotkit.transport.api.common.queue.SendResultCallback;
import cn.iotab.iotkit.transport.api.driver.TransportDriverService;
import cn.iotab.iotkit.transport.api.common.message.msg.rpc.AuthResponseMsg;
import cn.iotab.iotkit.transport.api.common.message.msg.rpc.AuthX509RequestMsg;
import cn.iotab.iotkit.transport.api.common.util.SslUtil;
import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@Slf4j
public class MqttX509TrustManager implements X509TrustManager {

    private final X509TrustManager trustManager;
    private final TransportDriverService transportDriverService;

    MqttX509TrustManager(X509TrustManager trustManager, TransportDriverService transportDriverService) {
        this.trustManager = trustManager;
        this.transportDriverService = transportDriverService;
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return trustManager.getAcceptedIssuers();
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
                                   String authType) throws CertificateException {
        trustManager.checkServerTrusted(chain, authType);
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain,
                                   String authType) throws CertificateException {
        String credentialsBody = null;
        for (X509Certificate cert : chain) {
            try {
                String strCert = SslUtil.getX509CertificateString(cert);
                String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
                final String[] credentialsBodyHolder = new String[1];
                CountDownLatch latch = new CountDownLatch(1);
                transportDriverService.authenticate(AuthX509RequestMsg.builder().hash(sha3Hash).build(),
                        new SendResultCallback<AuthResponseMsg>() {
                            @Override
                            public void onSuccess(AuthResponseMsg msg) {
                                if (msg.getCredentials() != null && !msg.getCredentials().isEmpty()) {
                                    credentialsBodyHolder[0] = msg.getCredentials();
                                }
                                latch.countDown();
                            }

                            @Override
                            public void onError(Throwable e) {
                                log.error(e.getMessage(), e);
                                latch.countDown();
                            }
                        });
                latch.await(10, TimeUnit.SECONDS);
                if (strCert.equals(credentialsBodyHolder[0])) {
                    credentialsBody = credentialsBodyHolder[0];
                    break;
                }
            } catch (InterruptedException | IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        if (credentialsBody == null) {
            throw new CertificateException("Invalid Device Certificate");
        }
    }
}